using System;
using System.Collections.Generic;
using AnimatingHair.Entity.PhysicalEntity;
using OpenTK;

namespace AnimatingHair.Auxiliary
{
    class VoxelGrid
    {
        // TODO: zmen na private, public su len kvoli debugu
        public double cellSize;
        public int[] cellCount = new int[ 3 ];
        public Vector3d cornerLocation;
        public VoxelGridCell[ , , ] grid;

        private VoxelGridCell outside;

        public VoxelGrid(
            double h2,
            double[] size,
            Vector3d cornerLocation
            // TODO: pridat nejake vstupy urcujuce rozmery voxel gridu 
            )
        {
            this.cornerLocation = cornerLocation;

            cellSize = 2 * h2;

            cellCount[ 0 ] = (int)Math.Ceiling( size[ 0 ] / cellSize );
            cellCount[ 1 ] = (int)Math.Ceiling( size[ 1 ] / cellSize );
            cellCount[ 2 ] = (int)Math.Ceiling( size[ 2 ] / cellSize );

            outside = new VoxelGridCell( -1, -1, -1 );

            grid = new VoxelGridCell[ cellCount[ 0 ], cellCount[ 1 ], cellCount[ 2 ] ];

            for ( int i = 0; i < cellCount[ 0 ]; i++ )
            {
                for ( int j = 0; j < cellCount[ 1 ]; j++ )
                {
                    for ( int k = 0; k < cellCount[ 2 ]; k++ )
                    {
                        grid[ i, j, k ] = new VoxelGridCell( i, j, k );
                    }
                }
            }
        }

        public LinkedList<SPHParticle> FindNeighbors( SPHParticle particle )
        {
            int newX, newY, newZ;
            bool edge = false;

            int x = particle.ContainedIn.X;
            int y = particle.ContainedIn.Y;
            int z = particle.ContainedIn.Z;
            VoxelGridCell currentCell;

            LinkedList<SPHParticle> result = new LinkedList<SPHParticle>();

            for ( int i = -1; i <= 1; i++ )
            {
                for ( int j = -1; j <= 1; j++ )
                {
                    for ( int k = -1; k <= 1; k++ )
                    {
                        newX = x + i;
                        newY = y + j;
                        newZ = z + k;

                        if ( newX < 0 || newX >= cellCount[ 0 ] || newY < 0 || newY >= cellCount[ 1 ] || newZ < 0 || newZ >= cellCount[ 2 ] )
                        {
                            edge = true;
                        }
                        else
                        {
                            currentCell = grid[ newX, newY, newZ ];
                            foreach ( SPHParticle p in currentCell.Particles )
                            {
                                //yield return p;
                                result.AddLast( p );
                            }
                        }
                    }
                }
            }

            if ( edge )
            {
                foreach ( SPHParticle p in outside.Particles )
                {
                    //yield return p;
                    result.AddLast( p );
                }
            }

            return result;
        }

        public void UpdateParticle( SPHParticle particle )
        {
            VoxelGridCell oldContainer = particle.ContainedIn;
            if ( oldContainer != null )
                oldContainer.RemoveElement( particle );

            VoxelGridCell newContainer = getCell( particle.Position );
            newContainer.AddElement( particle );
            particle.ContainedIn = newContainer;
        }

        private VoxelGridCell getCell( Vector3d location )
        {
            int x, y, z;

            //x = (int)Math.Floor( (location.X - cornerLocation.X) * (cellCount[ 0 ] / cellSize) );
            //y = (int)Math.Floor( (location.Y - cornerLocation.Y) * (cellCount[ 1 ] / cellSize) );
            //z = (int)Math.Floor( (location.Z - cornerLocation.Z) * (cellCount[ 2 ] / cellSize) );
            x = (int)Math.Floor( (location.X - cornerLocation.X) / cellSize );
            y = (int)Math.Floor( (location.Y - cornerLocation.Y) / cellSize );
            z = (int)Math.Floor( (location.Z - cornerLocation.Z) / cellSize );

            if ( x < 0 || x >= cellCount[ 0 ] || y < 0 || y >= cellCount[ 1 ] || z < 0 || z >= cellCount[ 2 ] )
            {
                return outside;
            }

            return grid[ x, y, z ];
        }
    }
}